package script

import (
	"bytes"
	"context"
	"crypto/md5"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"
	"wx_spider_go/config"
	"wx_spider_go/logger"
	"wx_spider_go/models"
	"wx_spider_go/utils"

	"github.com/PuerkitoBio/goquery"
	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
)

// 获取文章 HTML 内容
func fetchArticleHTML(url string) (*goquery.Document, error) {
	res, err := utils.GetClient().NewRequest().SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0").
		Get(url)
	if err != nil {
		return nil, err
	}
	if res.StatusCode() != 200 {
		return nil, fmt.Errorf("HTTP status code: %d", res.StatusCode())
	}

	doc, err := goquery.NewDocumentFromReader(bytes.NewReader(res.Body()))
	if err != nil {
		return nil, err
	}

	// 查找 js_content div
	contentDiv := doc.Find("#js_content")
	if contentDiv.Length() == 0 {
		return nil, fmt.Errorf("content div not found")
	}
	return doc, nil
}

func uploadImage(spiderConfig models.Config, imgURL, bucket, imgDir string) (string, error) {
	imgDir = strings.Trim(imgDir, "/")
	if imgDir == "" {
		imgDir = "default"
	}
	imgDir = fmt.Sprintf("%s/%s", imgDir, time.Now().Format("2006/01/02"))
	// 清理 bucket 名称
	bucket = strings.ReplaceAll(bucket, "_", "-")
	bucket = strings.TrimSpace(bucket)
	// 初始化 MinIO 客户端
	minioClient, err := minio.New(spiderConfig.MinioEndpoint, &minio.Options{
		Creds:  credentials.NewStaticV4(config.GlobalConfig.MinIO.AccessKey, config.GlobalConfig.MinIO.SecretKey, ""),
		Secure: false,
	})
	if err != nil {
		return "", fmt.Errorf("failed to create MinIO client: %v", err)
	}

	// 检查 bucket 是否存在，不存在则创建
	exists, err := minioClient.BucketExists(context.Background(), bucket)
	if err != nil {
		return "", err
	}
	if !exists {
		err = minioClient.MakeBucket(context.Background(), bucket, minio.MakeBucketOptions{})
		if err != nil {
			return "", err
		}
		logger.MinioInfo("存储桶 '%s' 创建成功", bucket)
	}

	// 下载图片
	resp, err := http.Get(imgURL)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return "", fmt.Errorf("failed to download image: %d", resp.StatusCode)
	}

	imgData, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	// 生成图片名称
	hash := md5.Sum(imgData)
	imgName := fmt.Sprintf("%s/%x.jpeg", imgDir, hash)

	// 上传到 MinIO
	_, err = minioClient.PutObject(context.Background(), bucket, imgName, bytes.NewReader(imgData), int64(len(imgData)), minio.PutObjectOptions{ContentType: "image/jpeg"})
	if err != nil {
		return "", err
	}

	logger.ImageInfo("图片 %s 上传成功", imgName)
	return fmt.Sprintf("%s/%s/%s", config.GlobalConfig.OSS.Domain, bucket, imgName), nil
}

func processImages(config models.Config, doc *goquery.Document, saveImgNum int, bucket, imgDir string) error {
	imgTags := doc.Find("img")
	imgCount := 0

	imgTags.Each(func(i int, s *goquery.Selection) {
		if imgCount >= saveImgNum {
			s.Remove()
			return
		}

		imgSrc := s.AttrOr("data-src", "")
		if imgSrc == "" {
			imgSrc = s.AttrOr("src", "")
		}

		if imgSrc == "" {
			s.Remove()
			return
		}

		newURL, err := uploadImage(config, imgSrc, bucket, imgDir)
		if err != nil {
			logger.Error("图片上传失败: %s - %v", imgSrc, err)
			s.Remove()
			return
		}
		s.SetAttr("src", newURL)
		s.RemoveAttr("data-src")
		imgCount++
	})
	return nil
}

func HandlerArticle(article models.Article, config models.Config) error {
	name := config.Name
	articleIndex := strings.TrimSpace(config.ESIndex)
	saveImgNum := config.SaveImgNum
	minioBucket := config.MinioBucket
	imgDir := config.ImgDir
	url := article.Link

	// 获取文章 ID
	aid := article.AID
	if aid == "" {
		aid = article.ID
	}
	if aid == "" {
		return errors.New("文章ID不能为空")
	}

	// 生成唯一标识
	key := fmt.Sprintf("%s:%s", name, aid)
	esDocID := fmt.Sprintf("%x", md5.Sum([]byte(key)))
	title := article.Title

	// 检查文章是否已入库
	ok := utils.EsDocExists(config.ESHost, articleIndex, esDocID)
	if ok {
		logger.Warn("文章 '%s' 已入库，跳过", title)
		return nil
	}

	// 获取文章内容
	doc, err := fetchArticleHTML(url)
	if err != nil {
		logger.Error("获取文章 '%s' 内容失败: %v", title, err)
		return err
	}

	// 获取 js_content div
	contentDiv := doc.Find("#js_content")
	if contentDiv.Length() == 0 {
		logger.Warn("文章 '%s' 内容为空", title)
		return nil
	}
	// 移除 style 属性
	contentDiv.RemoveAttr("style")
	// 获取文本内容
	content := contentDiv.Text()
	if len([]rune(content)) <= 10 {
		logger.Warn("文章 %s 内容太少，跳过", title)
		return nil
	}
	// 处理图片
	err = processImages(config, doc, saveImgNum, minioBucket, imgDir)
	if err != nil {
		logger.Error("处理图片失败: %v", err)
	}

	// 获取处理后的 HTML 内容
	contentHTML, err := contentDiv.Html()
	if err != nil {
		return err
	}

	// 创建文档
	document := map[string]any{
		"@timestamp": time.Now().Format("2006-01-02T15:04:05-07:00"),
		"title":      title,
		"content":    contentHTML,
		"source":     url,
		"gzh_name":   name,
		"pubnum":     0,
	}
	// 添加其他字段
	if config.OtherMap != nil {
		for k, v := range config.OtherMap {
			document[k] = v
		}
	}
	createDoc, err := utils.EsCreateDoc(document, config.ESHost, config.ESIndex, esDocID)
	if err != nil {
		return err
	}
	if createDoc.StatusCode()/100 == 2 {
		logger.ESInfo("文章 '%s' 入库成功", title)
		return nil
	} else {
		logger.Error("文章入库失败：%s", createDoc.String())
	}
	return nil
}
